<template>
  <div
    class="fixed right-4 z-[9999] space-y-2 pointer-events-none"
    style="top: 25vh"
  >
    <div
      v-for="notification in processedNotifications"
      :key="notification.id"
      :data-notification-id="notification.id"
      class="notification-item pointer-events-auto"
      :class="{
        'fade-out': notification.fadeOut && !notification.isHovered,
        'slide-out': notification.isSliding,
        dragging: notification.isDragging,
      }"
      :style="{
        transform: notification.transform || 'none',
        opacity: notification.opacity !== undefined ? notification.opacity : 1,
      }"
      @mouseenter="handleMouseEnter(notification)"
      @mouseleave="handleMouseLeave(notification)"
    >
      <div
        class="bg-white dark:bg-gray-800 rounded-lg shadow-lg border border-gray-200 dark:border-gray-700 w-[360px] overflow-hidden group relative"
        :class="{ 'shadow-xl': notification.isDragging }"
      >
        <!-- 通知头部 - 只保留 mousedown 事件 -->
        <div
          class="flex items-center justify-between px-4 py-3 bg-gray-50 dark:bg-gray-800/50 cursor-grab drag-handle"
          @mousedown="handleMouseDown($event, notification)"
        >
          <div class="flex items-center space-x-2">
            <el-avatar
              :size="24"
              :src="notification.avatar"
              class="flex-shrink-0"
            >
              <img src="/default-avatar.png" alt="头像" />
            </el-avatar>
            <span class="font-medium text-gray-900 dark:text-white select-none">
              {{ notification.title }}
              <span
                v-if="notification.count > 1"
                class="ml-2 text-xs text-gray-500 dark:text-gray-400"
              >
                ({{ notification.count }}条消息)
              </span>
            </span>
          </div>
          <span class="text-xs text-gray-500 dark:text-gray-400 select-none">{{
            formatTime(notification.time)
          }}</span>
        </div>

        <!-- 通知内容 -->
        <div class="px-4 py-3">
          <p class="text-gray-700 dark:text-gray-300 line-clamp-2">
            {{ notification.content }}
            <span
              v-if="notification.count > 1"
              class="text-gray-500 dark:text-gray-400 text-sm block mt-1"
            >
              等{{ notification.count }}条新消息
            </span>
          </p>
        </div>

        <!-- 通知底部 -->
        <div
          class="flex items-center justify-between px-4 py-2 bg-gray-50/50 dark:bg-gray-800/30"
        >
          <div class="flex items-center space-x-2">
            <el-icon
              :class="
                notification.type === 'system'
                  ? 'text-blue-500'
                  : 'text-green-500'
              "
            >
              <component :is="getNotificationIcon(notification.type)" />
            </el-icon>
            <span class="text-xs text-gray-500 dark:text-gray-400">
              {{ notification.type === "system" ? "系统通知" : "互动消息" }}
            </span>
          </div>
          <div
            class="text-xs text-primary-600 dark:text-primary-400 opacity-0 group-hover:opacity-100 transition-opacity duration-200 cursor-pointer"
            @click="handleViewAll(notification)"
          >
            点击查看全部
          </div>
        </div>
      </div>
    </div>
  </div>
</template>

<script setup>
import { ref, onMounted, computed, onBeforeUnmount } from "vue";
import { useRouter, useRoute } from "vue-router";
import { Bell, ChatDotRound } from "@element-plus/icons-vue";
import { useUserStore } from "@/stores/user";
import { useNotificationStore } from "@/stores/notification";
import axios from "@/api/axios";
import dayjs from "dayjs";
import relativeTime from "dayjs/plugin/relativeTime";
import "dayjs/locale/zh-cn";

dayjs.extend(relativeTime);
dayjs.locale("zh-cn");

const router = useRouter();
const route = useRoute();
const userStore = useUserStore();
const notificationStore = useNotificationStore();
const notifications = ref([]);
const timeoutIds = ref(new Map());
const animationsMap = ref(new Map()); // 存储动画实例
const isDragging = ref(false);
const startX = ref(0);
const startY = ref(0);
const lastTime = ref(0);
const lastDeltaX = ref(0);
const lastDeltaY = ref(0);
const velocityX = ref(0);
const velocityY = ref(0);
const activeNotification = ref(null);
const dragThrottle = ref(false);

// 判断是否应该显示通知
const shouldShowNotification = computed(() => {
  // 需要隐藏通知的路由
  const hideNotificationPaths = ["/login", "/register", "/forgot-password"];

  // 如果是需要隐藏通知的路由，或者用户未登录，则不显示通知
  return !hideNotificationPaths.includes(route.path) && userStore.isLoggedIn;
});

// 计算处理后的通知列表（最多显示3条，相同来源折叠）
const processedNotifications = computed(() => {
  const sourceMap = new Map();
  const result = [];

  // 按来源分组
  notifications.value.forEach((notification) => {
    const sourceKey = `${notification.type}-${notification.title}`;
    if (!sourceMap.has(sourceKey)) {
      sourceMap.set(sourceKey, {
        ...notification,
        count: 1,
      });
    } else {
      const existing = sourceMap.get(sourceKey);
      existing.count++;
      existing.content = notification.content; // 更新为最新消息
      existing.time = notification.time; // 更新为最新时间
    }
  });

  // 转换为数组并限制数量
  const sortedNotifications = Array.from(sourceMap.values())
    .sort((a, b) => b.time - a.time)
    .slice(0, 3);

  return sortedNotifications;
});

// 获取通知图标
const getNotificationIcon = (type) => {
  return type === "system" ? Bell : ChatDotRound;
};

// 格式化时间
const formatTime = (time) => {
  return dayjs(time).fromNow();
};

// 节流函数
const throttle = (callback, limit = 16) => {
  if (dragThrottle.value) return;
  dragThrottle.value = true;
  setTimeout(() => {
    callback();
    dragThrottle.value = false;
  }, limit);
};

// 获取元素
const getNotificationElement = (notificationId) => {
  return document.querySelector(`[data-notification-id="${notificationId}"]`);
};

// 处理鼠标进入
const handleMouseEnter = (notification) => {
  notification.isHovered = true;

  // 取消当前的自动关闭定时器
  if (timeoutIds.value.has(notification.id)) {
    clearTimeout(timeoutIds.value.get(notification.id).fadeOutTimer);
    clearTimeout(timeoutIds.value.get(notification.id).removeTimer);
    timeoutIds.value.delete(notification.id);
  }

  // 取消正在进行的动画
  cancelNotificationAnimation(notification);
};

// 取消正在进行的动画
const cancelNotificationAnimation = (notification) => {
  if (animationsMap.value.has(notification.id)) {
    const animation = animationsMap.value.get(notification.id);
    if (animation && animation.cancel) {
      animation.cancel();
    }
    animationsMap.value.delete(notification.id);
  }
};

// 处理鼠标按下
const handleMouseDown = (event, notification) => {
  if (event.button !== 0) return;

  // 阻止事件冒泡和默认行为
  event.preventDefault();
  event.stopPropagation();

  // 获取元素
  const element = getNotificationElement(notification.id);
  if (!element) return;

  // 取消当前的任何动画
  cancelNotificationAnimation(notification);

  // 记录初始状态
  isDragging.value = true;
  activeNotification.value = notification;
  notification.isDragging = true;

  // 记录初始位置和时间
  startX.value = event.clientX;
  startY.value = event.clientY;
  lastTime.value = Date.now();
  lastDeltaX.value = 0;
  lastDeltaY.value = 0;
  velocityX.value = 0;
  velocityY.value = 0;

  // 添加拖动效果 - 使用Web Animations API
  const pressAnimation = element.animate(
    [
      { transform: "scale(1)", boxShadow: "0 0 0 rgba(0,0,0,0.2)" },
      { transform: "scale(0.98)", boxShadow: "0 10px 20px rgba(0,0,0,0.1)" },
    ],
    {
      duration: 150,
      fill: "forwards",
      easing: "cubic-bezier(0.25, 1, 0.5, 1)",
    },
  );

  // 存储动画实例
  animationsMap.value.set(notification.id, pressAnimation);

  // 添加全局事件监听
  document.addEventListener("mousemove", handleGlobalMouseMove, {
    passive: false,
  });
  document.addEventListener("mouseup", handleGlobalMouseUp);
};

// 全局鼠标移动处理 (使用Web Animations API)
const handleGlobalMouseMove = (event) => {
  event.preventDefault();

  if (!isDragging.value || !activeNotification.value) return;

  throttle(() => {
    const notification = activeNotification.value;
    if (!notification) return;

    const element = getNotificationElement(notification.id);
    if (!element) return;

    // 取消当前的动画
    cancelNotificationAnimation(notification);

    // 计算移动距离和速度
    const now = Date.now();
    const deltaTime = now - lastTime.value;
    if (deltaTime <= 0) return;

    const deltaX = event.clientX - startX.value;
    const deltaY = event.clientY - startY.value;

    // 更新速度（像素/毫秒）
    velocityX.value = (deltaX - lastDeltaX.value) / Math.max(deltaTime, 1);
    velocityY.value = (deltaY - lastDeltaY.value) / Math.max(deltaTime, 1);

    // 更新上次状态
    lastDeltaX.value = deltaX;
    lastDeltaY.value = deltaY;
    lastTime.value = now;

    // 计算拖动效果参数
    const distance = Math.sqrt(deltaX ** 2 + deltaY ** 2);
    const rotate = Math.min(Math.max(deltaX * 0.05, -8), 8); // 限制旋转角度
    const scale = Math.max(0.92, 0.98 - distance / 2500);
    const opacity = Math.max(0.8, 1 - distance / 1000);

    // 应用动画效果 - 直接设置样式（避免创建多个动画实例）
    element.style.transform = `translate3d(${deltaX}px, ${deltaY}px, 0) scale(${scale}) rotate(${rotate}deg)`;
    element.style.opacity = opacity;

    // 添加阴影效果
    const shadowBlur = 10 + Math.min(distance / 30, 20);
    element.style.boxShadow = `0 ${shadowBlur}px ${shadowBlur * 2}px rgba(0, 0, 0, ${0.1 + distance / 2000})`;
  });
};

// 全局鼠标松开处理 (使用Web Animations API)
const handleGlobalMouseUp = (event) => {
  if (!isDragging.value || !activeNotification.value) return;

  const notification = activeNotification.value;
  const element = getNotificationElement(notification.id);

  if (!element) {
    // 清理状态
    isDragging.value = false;
    notification.isDragging = false;
    activeNotification.value = null;
    document.removeEventListener("mousemove", handleGlobalMouseMove);
    document.removeEventListener("mouseup", handleGlobalMouseUp);
    return;
  }

  // 取得当前状态
  const deltaX = lastDeltaX.value;
  const deltaY = lastDeltaY.value;
  const speed = Math.sqrt(velocityX.value ** 2 + velocityY.value ** 2);
  const distance = Math.sqrt(deltaX ** 2 + deltaY ** 2);

  // 重置拖动状态
  isDragging.value = false;
  notification.isDragging = false;

  // 取消当前的动画
  cancelNotificationAnimation(notification);

  // 判断是否应该滑出 (基于距离和速度)
  const thresholdDistance = 100; // 滑出的距离阈值
  const thresholdSpeed = 0.4; // 滑出的速度阈值
  const shouldSlideOut =
    distance > thresholdDistance || (distance > 40 && speed > thresholdSpeed);

  // 创建新的动画
  let animation;

  if (shouldSlideOut) {
    // 滑出动画
    const angle = Math.atan2(velocityY.value, velocityX.value);
    const throwFactor = Math.min(Math.max(speed * 1200, 800), 1500);
    const endX = deltaX + Math.cos(angle) * throwFactor;
    const endY = deltaY + Math.sin(angle) * throwFactor;
    const rotateDir = deltaX >= 0 ? 1 : -1;

    animation = element.animate(
      [
        {
          transform: `translate3d(${deltaX}px, ${deltaY}px, 0) scale(0.98) rotate(${deltaX * 0.05}deg)`,
          opacity: element.style.opacity || 1,
          boxShadow: element.style.boxShadow || "none",
        },
        {
          transform: `translate3d(${endX}px, ${endY}px, 0) scale(0.5) rotate(${rotateDir * 25}deg)`,
          opacity: 0,
          boxShadow: "0 20px 40px rgba(0,0,0,0)",
        },
      ],
      {
        duration: 650,
        easing: "cubic-bezier(0.2, 0.8, 0.2, 1)",
        fill: "forwards",
      },
    );

    notification.isSliding = true;

    // 动画完成后移除通知
    animation.onfinish = () => {
      notification.isSliding = false;
      closeNotification(notification);
    };
  } else {
    // 回弹动画 - 使用弹性曲线
    animation = element.animate(
      [
        {
          transform: `translate3d(${deltaX}px, ${deltaY}px, 0) scale(${element.style.scale || 0.98}) rotate(${deltaX * 0.05}deg)`,
          boxShadow: element.style.boxShadow || "none",
        },
        {
          transform: "translate3d(0, 0, 0) scale(1) rotate(0deg)",
          boxShadow: "none",
        },
      ],
      {
        duration: 500,
        easing: "cubic-bezier(0.25, 1, 0.5, 1.3)", // 弹性回弹
        fill: "forwards",
      },
    );
  }

  // 存储动画实例
  animationsMap.value.set(notification.id, animation);

  // 清理事件监听
  document.removeEventListener("mousemove", handleGlobalMouseMove);
  document.removeEventListener("mouseup", handleGlobalMouseUp);
  activeNotification.value = null;
};

// 组件卸载前清理
onBeforeUnmount(() => {
  // 清理事件监听
  document.removeEventListener("mousemove", handleGlobalMouseMove);
  document.removeEventListener("mouseup", handleGlobalMouseUp);
  window.removeEventListener("show-notifications", handleShowNotifications);

  // 停止通知轮询
  notificationStore.stopPolling();

  // 清理所有计时器
  timeoutIds.value.forEach((timers) => {
    if (timers.fadeOutTimer) clearTimeout(timers.fadeOutTimer);
    if (timers.removeTimer) clearTimeout(timers.removeTimer);
  });
  timeoutIds.value.clear();

  // 取消所有动画
  animationsMap.value.forEach((animation) => {
    if (animation && animation.cancel) {
      animation.cancel();
    }
  });
  animationsMap.value.clear();
});

// 修改鼠标离开处理
const handleMouseLeave = (notification) => {
  notification.isHovered = false;

  // 如果正在拖动，不执行任何操作
  if (isDragging.value && activeNotification.value === notification) {
    return;
  }

  // 如果不在拖动和滑出状态，开始自动关闭计时
  if (!notification.isDragging && !notification.isSliding) {
    startNotificationTimer(notification);
  }
};

// 修改自动关闭的动画 (使用Web Animations API)
const startNotificationTimer = (notification) => {
  const fadeOutTimer = setTimeout(() => {
    const element = getNotificationElement(notification.id);
    if (!element || notification.isHovered || notification.isSliding) return;

    // 取消先前的动画
    cancelNotificationAnimation(notification);

    notification.isSliding = true;

    // 第一阶段：轻微上浮动画
    const floatAnimation = element.animate(
      [
        { transform: "translate3d(0, 0, 0) scale(1)", opacity: 1 },
        { transform: "translate3d(0, -10px, 0) scale(0.98)", opacity: 0.95 },
      ],
      {
        duration: 300,
        easing: "cubic-bezier(0.4, 0, 0.2, 1)",
        fill: "forwards",
      },
    );

    // 存储动画
    animationsMap.value.set(notification.id, floatAnimation);

    // 第一阶段完成后，开始第二阶段
    floatAnimation.onfinish = () => {
      // 再次检查通知状态
      if (notification.isHovered) {
        notification.isSliding = false;
        cancelNotificationAnimation(notification);

        // 恢复初始状态
        const resetAnimation = element.animate(
          [
            {
              transform: "translate3d(0, -10px, 0) scale(0.98)",
              opacity: 0.95,
            },
            { transform: "translate3d(0, 0, 0) scale(1)", opacity: 1 },
          ],
          {
            duration: 250,
            easing: "cubic-bezier(0.4, 0, 0.2, 1)",
            fill: "forwards",
          },
        );

        animationsMap.value.set(notification.id, resetAnimation);
        return;
      }

      // 第二阶段：向右滑出动画
      const slideOutAnimation = element.animate(
        [
          { transform: "translate3d(0, -10px, 0) scale(0.98)", opacity: 0.95 },
          {
            transform: "translate3d(200%, 0, 0) scale(0.8) rotate(15deg)",
            opacity: 0,
          },
        ],
        {
          duration: 700,
          easing: "cubic-bezier(0.3, 0.85, 0.3, 1)",
          fill: "forwards",
        },
      );

      // 更新动画实例
      animationsMap.value.set(notification.id, slideOutAnimation);

      // 滑出动画完成后关闭通知
      slideOutAnimation.onfinish = () => {
        closeNotification(notification);
      };

      // 设置备份关闭计时器（以防动画完成事件未触发）
      const removeTimer = setTimeout(() => {
        if (notification.isSliding) {
          closeNotification(notification);
        }
      }, 750);

      // 更新定时器
      if (timeoutIds.value.has(notification.id)) {
        timeoutIds.value.set(notification.id, {
          ...timeoutIds.value.get(notification.id),
          removeTimer,
        });
      }
    };
  }, 4500);

  timeoutIds.value.set(notification.id, { fadeOutTimer });
};

// 修改关闭通知方法
const closeNotification = (notification) => {
  // 取消动画
  cancelNotificationAnimation(notification);

  // 从通知列表中移除
  const index = notifications.value.findIndex((n) => n.id === notification.id);
  if (index > -1) {
    notifications.value.splice(index, 1);
  }

  // 清理定时器
  if (timeoutIds.value.has(notification.id)) {
    const timers = timeoutIds.value.get(notification.id);
    if (timers.fadeOutTimer) clearTimeout(timers.fadeOutTimer);
    if (timers.removeTimer) clearTimeout(timers.removeTimer);
    timeoutIds.value.delete(notification.id);
  }
};

// 修改新通知的进入动画 (使用Web Animations API)
const addNotification = (notification) => {
  const id = Date.now();
  const newNotification = {
    id,
    time: new Date(),
    fadeOut: false,
    isHovered: false,
    isSliding: false,
    isDragging: false,
    ...notification,
  };

  // 添加到通知列表
  notifications.value.unshift(newNotification);

  // 等待DOM更新后执行进入动画
  setTimeout(() => {
    const element = getNotificationElement(id);
    if (!element) return;

    // 创建进入动画
    const enterAnimation = element.animate(
      [
        { transform: "translate3d(50px, 0, 0) scale(0.9)", opacity: 0 },
        { transform: "translate3d(0, 0, 0) scale(1)", opacity: 1 },
      ],
      {
        duration: 500,
        easing: "cubic-bezier(0.34, 1.56, 0.64, 1)",
        fill: "forwards",
      },
    );

    // 存储动画实例
    animationsMap.value.set(id, enterAnimation);
  }, 50);

  // 如果超过最大显示数量，移除最旧的非悬停通知
  const nonHoveredNotifications = notifications.value.filter(
    (n) => !n.isHovered,
  );
  if (nonHoveredNotifications.length > 3) {
    const oldestNonHovered =
      nonHoveredNotifications[nonHoveredNotifications.length - 1];
    notifications.value = notifications.value.filter(
      (n) => n.id !== oldestNonHovered.id,
    );
  }

  // 开始定时器
  startNotificationTimer(newNotification);
};

// 添加查看全部处理函数
const handleViewAll = (notification) => {
  // 根据当前路由路径判断所在界面
  const isAdminPath = route.path.startsWith("/admin");
  const isMerchantPath =
    route.path.startsWith("/merchant") &&
    route.path !== "/merchant/apply" &&
    route.path !== "/merchant/apply-form" &&
    route.path !== "/merchant/status";
  const isHotelAdminPath = route.path.startsWith("/hotel-admin");

  // 根据不同界面决定跳转路径
  let targetPath = "";
  if (isAdminPath) {
    targetPath = `/admin/notification`;
  } else if (isMerchantPath) {
    targetPath = `/merchant/notification`;
  } else if (isHotelAdminPath) {
    targetPath = `/hotel-admin/notification`;
  } else {
    // 默认为前台用户中心
    targetPath = `/user/messages?tab=${notification.type === "system" ? "system" : "interaction"}`;
  }

  router.push(targetPath);
};

// 获取不同身份用户的通知头像
const getAvatarByUserType = () => {
  if (userStore.isAdmin) {
    return "/admin-avatar.png";
  } else if (userStore.isMerchant) {
    if (userStore.isHotelMerchant) {
      return "/hotel-avatar.png";
    } else {
      return "/merchant-avatar.png";
    }
  } else {
    return "/system-avatar.png";
  }
};

// 处理来自通知状态管理的自定义事件
const handleShowNotifications = (event) => {
  if (event.detail && event.detail.notifications) {
    // 根据不同用户身份处理通知
    processNotificationsByUserType(event.detail.notifications);
  }
};

// 根据用户身份处理通知
const processNotificationsByUserType = (newNotifications) => {
  if (!Array.isArray(newNotifications) || !newNotifications.length) return;
  
  // 获取当前用户身份
  const userType = getUserType();
  
  // 根据不同身份生成适合的通知对象
  newNotifications.forEach(notification => {
    // 将API返回的通知对象转换为适合展示的格式
    const displayNotification = {
      id: notification.id,
      title: getNotificationTitleByUserType(notification, userType),
      content: notification.content,
      time: new Date(notification.create_time || notification.createTime || new Date()),
      type: mapNotificationType(notification.type, userType),
      avatar: getAvatarByUserType(),
      actionUrl: notification.action_url || notification.actionUrl,
    };
    
    // 将转换后的通知添加到全局通知组件中
    addNotification(displayNotification);
  });
};

// 根据用户身份获取通知标题
const getNotificationTitleByUserType = (notification, userType) => {
  // 可以根据不同的用户身份自定义通知标题
  const title = notification.title || notification.subject || '';
  
  if (userType === "admin") {
    return `管理员通知: ${title}`;
  } else if (userType === "merchant") {
    return `商户通知: ${title}`;
  } else if (userType === "hotel") {
    return `酒店通知: ${title}`;
  } else {
    return title;
  }
};

// 映射通知类型
const mapNotificationType = (type, userType) => {
  // 根据后端通知类型和用户身份，返回前端通知类型
  if (type === "pending" || type === "review" || type === "alert" || type === "system") {
    return "system";
  } else if (type === "order" || type === "finance") {
    return "interaction";
  } else {
    return type;
  }
};

// 获取当前用户类型
const getUserType = () => {
  if (userStore.isAdmin) {
    return "admin";
  } else if (userStore.isHotelMerchant) {
    return "hotel";
  } else if (userStore.isMerchant) {
    return "merchant";
  } else {
    return "user";
  }
};

// 获取实时通知
const fetchRealtimeNotifications = async () => {
  if (!userStore.isLoggedIn || !shouldShowNotification.value) return;
  
  try {
    // 根据用户类型获取不同的API路径
    const userType = getUserType();
    let apiPath = '';
    
    if (userType === 'admin') {
      apiPath = '/admin/notifications/realtime';
    } else if (userType === 'hotel') {
      apiPath = '/hotel-admin/notifications/realtime';
    } else if (userType === 'merchant') {
      apiPath = '/merchant/notifications/realtime';
    } else {
      apiPath = '/user/notifications/realtime';
    }
    
    const response = await axios.get(apiPath);
    
    if (response.data && response.data.list && response.data.list.length > 0) {
      // 处理新通知
      processNotificationsByUserType(response.data.list);
      
      // 更新未读数量
      await notificationStore.fetchUnreadCount();
    }
  } catch (error) {
    console.error('获取实时通知失败:', error);
  }
};

// 修改生命周期钩子
onMounted(() => {
  // 添加自定义事件监听器
  window.addEventListener("show-notifications", handleShowNotifications);
  
  // 只有用户登录时才初始化通知模块
  if (userStore.isLoggedIn) {
    notificationStore.initialize();
    
    // 开始轮询实时通知
    const pollingInterval = setInterval(() => {
      fetchRealtimeNotifications();
    }, 30000); // 每30秒轮询一次
    
    // 在组件卸载时清除轮询
    onBeforeUnmount(() => {
      clearInterval(pollingInterval);
    });
    
    // 首次获取通知
    fetchRealtimeNotifications();
  }
});

// 暴露方法供其他组件调用
defineExpose({
  addNotification,
});
</script>

<style lang="scss" scoped>
.notification-item {
  user-select: none;
  touch-action: none;
  transform-origin: center right;
  will-change: transform, opacity;
  backface-visibility: hidden;
  perspective: 1000px;
  position: relative;
  width: 360px;

  &::before {
    content: "";
    position: absolute;
    inset: 0;
    pointer-events: none;
    z-index: -1;
    opacity: 0;
    border-radius: inherit;
    box-shadow: 0 8px 24px rgba(0, 0, 0, 0.15);
    transition: opacity 0.3s ease;
  }

  &.dragging {
    cursor: grabbing;

    &::before {
      opacity: 1;
    }

    .drag-handle {
      cursor: grabbing !important;

      &::after {
        opacity: 1;
        background: linear-gradient(to right, rgba(0, 0, 0, 0.05), transparent);
      }
    }
  }

  .drag-handle {
    position: relative;
    cursor: grab;

    &::after {
      content: "";
      position: absolute;
      left: 0;
      right: 0;
      top: 0;
      bottom: 0;
      background: linear-gradient(
        to right,
        rgba(255, 255, 255, 0.1),
        transparent
      );
      opacity: 0;
      transition: opacity 0.2s ease;
    }

    &:hover::after {
      opacity: 1;
    }
  }

  &:active {
    cursor: grabbing;
  }
}

@media (max-height: 640px) {
  .notification-group {
    top: 20vh;
  }
}

@media (min-height: 1080px) {
  .notification-group {
    top: 30vh;
  }
}
</style>
